/**
@author		Jong Min, Lee
@date		  02/07/2014
@breif		toolbar property

copyright(c) Tae Young Soft. All right reserved. 2014
*/
#include "stdafx.h"
#include "Taurus_Viewer/ToolbarProperty.h"
#include <algorithm>
#include "Taurus_Common/IAppGlobalInstanceSet.h"
#include "Taurus_Common/MonitorAdmin.h"
#include "Taurus_Util/Errors.h"
#include "Taurus_Viewer/ViewerProperty.h"
#include "Taurus_Viewer/Logger.h"
#include "Taurus_Viewer/VGlobalInstanceSet.h"

#define TOOLBAR_PROPERTY_NAME         _T("toolbar_property")
#define TOOLBOXES_PROPERTY_NAME       _T("toolboxes")
#define TOOLBAR_POS_PROPERTY_NAME     _T("toolbar_pos")
#define TOOLBOX_PROPERTY_NAME         _T("toolbox")
#define TOOLBOX_NAME_ATTR             _T("name")
#define TOOL_PROPERTY_NAME            _T("tool")
#define TOOL_ID_ATTR                  _T("id")
#define TOOLBAR_POS_ATTR              _T("pos")

CToolbarProperty* CToolbarProperty::m_pInstance = NULL;

CToolbarProperty::CToolbarProperty()
  : m_enumPrestTBPos(ToolbarPositionLeft)
{}

CToolbarProperty::~CToolbarProperty()
{
  destroyAllProperties();
}

CToolbarProperty* CToolbarProperty::getInstance()
{
  if(m_pInstance == NULL)
    _TRY_ALLOC(m_pInstance, CToolbarProperty);

  return m_pInstance;
}

DWORD CToolbarProperty::importProperty()
{
  //destroy all of current properties
  destroyAllProperties();

  DWORD dwError = ERROR_SUCCESS;

  CViewerProperty* pVProp = CViewerProperty::getInstance();
  //find toolbar property xml node
  CString strXPath;
  strXPath.Format(_T("//%s/%s"), VIEWER_PROPERTY_NAME, TOOLBAR_PROPERTY_NAME);
  MSXML2::IXMLDOMNodePtr pTBPropNode = pVProp->getProperty(strXPath);
  BOOL bMustUpdate = FALSE;
  if(pTBPropNode == NULL)
  {
    TAURUS_VIEWER_WARN("CToolbarProperty::importProperty() :"
      <<"can't find toolbar property xml node. "
      <<"initialize toolbar property to default.");

    dwError = initToDefault();
    if(dwError != ERROR_SUCCESS)
    {
      //destroy all of current properties
      destroyAllProperties();
      return dwError;
    }

    bMustUpdate = TRUE;
  }
  else
  {
    dwError = importProperty(pTBPropNode, bMustUpdate);
    if(dwError != ERROR_SUCCESS)
    {
      TAURUS_VIEWER_WARN("CToolbarProperty::importProperty() :"
        << "can't import toolbar property xml node.");
      return dwError;
    }
  }

  if(bMustUpdate)
    exportProperty();

  //toolbar visibility
  IAppGlobalInstanceSet* theAGSet = CVGlobalInstanceSet::getInstance()->getAppGlobalInstanceSet();
  CMonitorAdmin* theMAdmin = theAGSet->getMonitorAdmin();
  unsigned numMonitors = theMAdmin->getNumVisibleMonitors();
  try
  {
    m_theTBVisibilities.reserve(numMonitors);
    for(unsigned int i = 0; i < numMonitors; ++i)
      m_theTBVisibilities.push_back(TRUE);
  }
  catch(...)
  {
    destroyAllProperties();
    return ERROR_SUCCESS;
  }
  
  return ERROR_SUCCESS;
}

DWORD CToolbarProperty::setPresetToolbarPos(ToolbarPosition enumPos)
{
  if(enumPos <= ToolbarPositionFirstInvalid ||
     enumPos >= ToolbarPositionLastInvalid)
     return ERROR_INVALID_PARAMETER;

  m_enumPrestTBPos = enumPos;
  return ERROR_SUCCESS;
}

DWORD CToolbarProperty::getTBVisibility(const unsigned int index, BOOL& bVisible) const
{
  if(index >= m_theTBVisibilities.size()) return ERROR_SUCCESS;

  bVisible = m_theTBVisibilities[index];
  return ERROR_SUCCESS;
}

DWORD CToolbarProperty::setTBVisibility(const unsigned int index, BOOL visible)
{
  if(index >= m_theTBVisibilities.size()) return ERROR_INVALID_PARAMETER;

  m_theTBVisibilities[index] = visible;
  return ERROR_SUCCESS;
}

CFontProperty CToolbarProperty::getHiddenToolbarFontProperty(MonitorResolutionType resType) const
{
  CFontProperty font;
  font.color = RGB(255, 255, 255);
  font.iBold = 1;
  font.iItalic = 0;
  font.iSize = (int)((double)14 * (resType / 100));
  font.strFaceName = _T("Thahoma");

  return font;
}

DWORD CToolbarProperty::initToDefault()
{
  DWORD dwError = initToolboxPropertiesToDefault();
  if(dwError != ERROR_SUCCESS)
    return dwError;

  dwError = initPresetTBPosToDefault();
  return dwError;
}

DWORD CToolbarProperty::importProperty(MSXML2::IXMLDOMNodePtr pPropertyNode, 
                                       BOOL& bMustUpdate)
{
  if(pPropertyNode == NULL)
    return ERROR_INVALID_PARAMETER;

  //flag indicting whether property is initialized to default
  BOOL bInitDefault = FALSE;
  DWORD dwError = ERROR_SUCCESS;

  //find toolbox property node
  CString strXPath;
  strXPath.Format(_T("./%s"), TOOLBOXES_PROPERTY_NAME);
  MSXML2::IXMLDOMNodePtr pToolboxPropNode = XmlFunc::SelectSingleNodeInXmlNode(
    pPropertyNode, strXPath);
  //if toolbox property node is not exist, 
  //initialize toolbox property to default
  if(pToolboxPropNode == nullptr)
  {
    dwError = initToolboxPropertiesToDefault();
    if(dwError != ERROR_SUCCESS)
      return dwError;

    bInitDefault = TRUE;
  }
  else
  {
    dwError = importToolboxProperties(pToolboxPropNode);
    if(dwError != ERROR_SUCCESS)
      return dwError;
  }

  //find toolbar position property node
  strXPath.Format(_T("./%s"), TOOLBAR_POS_PROPERTY_NAME);
  MSXML2::IXMLDOMNodePtr pTBPosPropNode = XmlFunc::SelectSingleNodeInXmlNode(
    pPropertyNode, strXPath);
  //if toolbar position property is not exist,
  //initialize toolbar position property to default
  if(pTBPosPropNode == nullptr)
  {
    dwError = initPresetTBPosToDefault();
    if(dwError != ERROR_SUCCESS)
      return dwError;

    bInitDefault = TRUE;
  }
  else
  {
    dwError = importPresetTBPos(pTBPosPropNode);
    if(dwError != ERROR_SUCCESS)
      return dwError;
  }

  bMustUpdate = bInitDefault;
  return ERROR_SUCCESS;
}

DWORD CToolbarProperty::initToolboxPropertiesToDefault()
{
  CVGlobalInstanceSet* pVGSet = CVGlobalInstanceSet::getInstance();
  CToolDataAdmin* pTAdmin = pVGSet->getToolDataAdmin();

  return pTAdmin->createDefaultToolboxData(m_listToolboxes);
}

DWORD CToolbarProperty::importToolboxProperties(MSXML2::IXMLDOMNodePtr pPropertyNode)
{
  if(pPropertyNode == nullptr)
    return ERROR_INVALID_PARAMETER;

  //find toolbox property nodes
  CString strXPath;
  strXPath.Format(_T("./%s"), TOOLBOX_PROPERTY_NAME);
  MSXML2::IXMLDOMNodeListPtr plistToolboxNode = XmlFunc::SelectNodesInXmlNode(
    pPropertyNode, strXPath);
  if(plistToolboxNode == NULL)
    return ERROR_SUCCESS;

  long nNumToolboxNodes = plistToolboxNode->Getlength();
  if(nNumToolboxNodes <= 0)
    return ERROR_SUCCESS;

  //create custom toolbox data
  DWORD dwError = ERROR_SUCCESS;
  for(long nNodeIndex = 0; nNodeIndex < nNumToolboxNodes; ++nNodeIndex)
  {
    MSXML2::IXMLDOMNodePtr pToolboxNode = plistToolboxNode->Getitem(nNodeIndex);
    if(pToolboxNode == NULL)
      continue;

    CToolboxData* pToolboxData = NULL;
    dwError = createCustomToolbox(pToolboxNode, pToolboxData);
    if(dwError != ERROR_SUCCESS)
      return dwError;

    _TRY_BEGIN
    m_listToolboxes.push_back(pToolboxData);
    _CATCH_ALL
    _DELETE(pToolboxData);
    return ERROR_NOT_ENOUGH_MEMORY;
    _CATCH_END
  }

  return ERROR_SUCCESS;
}

DWORD CToolbarProperty::createCustomToolbox(MSXML2::IXMLDOMNodePtr pToolboxNode, 
                                            CToolboxData*& pOutToolbox)
{
  if(pToolboxNode == NULL)
    return ERROR_INVALID_PARAMETER;

  //get toolbox name
  CString strName;
  XmlFunc::GetAttributeString(pToolboxNode, TOOLBOX_NAME_ATTR, strName);
  if(strName.IsEmpty())
    return ERROR_INVALID_PARAMETER;

  //allocate memory for toolbox data
  CToolboxData* pToolboxData = NULL;
  _TRY_ALLOC(pToolboxData, CToolboxData);
  if(pToolboxData == NULL)
    return ERROR_NOT_ENOUGH_MEMORY;

  pToolboxData->setName(strName);
  
  //find tool property nodes
  CString strXPath;
  strXPath.Format(_T("./%s"), TOOL_PROPERTY_NAME);
  MSXML2::IXMLDOMNodeListPtr plistToolNodes = XmlFunc::SelectNodesInXmlNode(
    pToolboxNode, strXPath);
  if(plistToolNodes == NULL || plistToolNodes->Getlength() <= 0)
  {
    pOutToolbox = pToolboxData;
    return ERROR_SUCCESS;
  }

  CVGlobalInstanceSet* pVGSet = CVGlobalInstanceSet::getInstance();
  CToolDataAdmin* pTAmdin = pVGSet->getToolDataAdmin();

  long nNumToolNodes = plistToolNodes->Getlength();
  for(long nNodeIndex = 0; nNodeIndex < nNumToolNodes; ++nNodeIndex)
  {
    MSXML2::IXMLDOMNodePtr pToolNode = plistToolNodes->Getitem(nNodeIndex);
    if(pToolNode == NULL) continue;

    int nToolID = -1;
    XmlFunc::GetAttributeInt(pToolNode, TOOL_ID_ATTR, nToolID);
    if(nToolID <= 0) continue;

    //search tool data by ID
    const CToolData* pToolData = pTAmdin->search_ID(static_cast<unsigned>(nToolID));
    if(pToolData == NULL) continue;

    //add tool data to toolbox data
    _TRY_BEGIN
    pToolboxData->addToolData(pToolData);
    _CATCH_ALL
    _DELETE(pToolboxData);
    return ERROR_NOT_ENOUGH_MEMORY;
    _CATCH_END
  }

  pOutToolbox = pToolboxData;
  return ERROR_SUCCESS;
}

DWORD CToolbarProperty::initPresetTBPosToDefault()
{
  m_enumPrestTBPos = ToolbarPositionLeft;
  return ERROR_SUCCESS;
}

DWORD CToolbarProperty::importPresetTBPos(MSXML2::IXMLDOMNodePtr pPropertyNode)
{
  if(pPropertyNode == nullptr) return ERROR_INVALID_PARAMETER;

  int iPos = static_cast<int>(ToolbarPositionFirstInvalid);
  XmlFunc::GetAttributeInt(pPropertyNode, TOOLBAR_POS_ATTR, iPos);
  if(iPos <= static_cast<int>(ToolbarPositionFirstInvalid) ||
     iPos >= static_cast<int>(ToolbarPositionLastInvalid))
     m_enumPrestTBPos = ToolbarPositionLeft;
  else
    m_enumPrestTBPos = static_cast<ToolbarPosition>(iPos);

  return ERROR_SUCCESS;
}

DWORD CToolbarProperty::exportProperty()
{
  MSXML2::IXMLDOMDocument2Ptr pPropertyDoc = XmlFunc::CreateXmlDoc(
    TOOLBAR_PROPERTY_NAME, NULL, NULL);
  if(pPropertyDoc == nullptr) return ERROR_XML_PARSER;

  MSXML2::IXMLDOMNodePtr pPropertyNode = XmlFunc::GetRoot(pPropertyDoc);
  if(pPropertyNode == nullptr) return ERROR_XML_PARSER;

  MSXML2::IXMLDOMNodePtr pToolboxesPropNode = XmlFunc::CreateNewChild(
    pPropertyNode, TOOLBOXES_PROPERTY_NAME);
  if(pToolboxesPropNode == nullptr) return ERROR_XML_PARSER;

  DWORD dwError = exportToolboxProperties(pToolboxesPropNode);
  if(dwError != ERROR_SUCCESS) return dwError;

  dwError = exportPresetTBPosProperty(pPropertyNode);
  if(dwError != ERROR_SUCCESS) return dwError;

  CViewerProperty* pVProp = CViewerProperty::getInstance();
  dwError = pVProp->updateProperty(VIEWER_PROPERTY_NAME,
                                   TOOLBAR_PROPERTY_NAME,
                                   pPropertyNode);
  return dwError;
}

DWORD CToolbarProperty::exportToolboxProperties(MSXML2::IXMLDOMNodePtr pParentNode)
{
  if(pParentNode == nullptr) return ERROR_XML_PARSER;

  for(auto pos = m_listToolboxes.begin(), e = m_listToolboxes.end(); pos != e; ++pos)
  {
    MSXML2::IXMLDOMNodePtr pToolboxNode = XmlFunc::CreateNewChild(
      pParentNode, TOOLBOX_PROPERTY_NAME);
    if(pToolboxNode == nullptr) return ERROR_XML_PARSER;

    if(!XmlFunc::SetAttributeString(pToolboxNode,
                                    TOOLBOX_NAME_ATTR,
                                    (*pos)->getName(),
                                    NULL,
                                    NULL))
    {
      return ERROR_XML_PARSER;
    }

    //create tool nodes
    DWORD dwError = ERROR_SUCCESS;
    (*pos)->forEachToolData(
      [&pToolboxNode, &dwError](const CToolData* pToolData)
    {
      MSXML2::IXMLDOMNodePtr pToolNode = XmlFunc::CreateNewChild(
        pToolboxNode, TOOL_PROPERTY_NAME);
      if(pToolNode == nullptr)
      {
        dwError = ERROR_XML_PARSER;
        return false;
      }

      if(!XmlFunc::SetAttributeInt(pToolNode,
                                   TOOL_ID_ATTR,
                                   static_cast<int>(pToolData->uID),
                                   NULL,
                                   NULL))
      {
        dwError = ERROR_XML_PARSER;
        return false;
      }

      return true;
    });

    if(dwError != ERROR_SUCCESS)
      return dwError;
  }

  return ERROR_SUCCESS;
}

DWORD CToolbarProperty::exportPresetTBPosProperty(MSXML2::IXMLDOMNodePtr pParentNode)
{
  if(pParentNode == nullptr) return ERROR_INVALID_PARAMETER;

  MSXML2::IXMLDOMNodePtr pTBPosNode = XmlFunc::CreateNewChild(
    pParentNode, TOOLBAR_POS_PROPERTY_NAME);
  if(pTBPosNode == nullptr) return ERROR_XML_PARSER;
  
  if(!XmlFunc::SetAttributeInt(pTBPosNode,
                               TOOLBAR_POS_ATTR,
                               m_enumPrestTBPos,
                               NULL,
                               NULL))
  {
    return ERROR_XML_PARSER;
  }

  return ERROR_SUCCESS;
}

void CToolbarProperty::destroyAllProperties()
{
  m_theTBVisibilities.clear();
  removeAllToolboxProperties();
}

void CToolbarProperty::removeAllToolboxProperties()
{
  for_each(m_listToolboxes.begin(), m_listToolboxes.end(),
    [](CToolboxData* pToolbox) { _DELETE(pToolbox); });
}